package utils;
import com.alibaba.fastjson.JSONObject; // 导入 JSON 库
import model.command.Command; // 导入 Command 类
import model.command.CommandPos; // 导入 CommandPos 类
import model.command.RmCommand; // 导入 RmCommand 类
import model.command.SetCommand; // 导入 SetCommand 类
import utils.CommandUtil; // 导入 CommandUtil 工具类
import utils.RandomAccessFileUtil; // 导入 RandomAccessFileUtil 工具类

import java.io.File; // 导入 File 类
import java.io.IOException; // 导入 IOException 类
import java.io.RandomAccessFile; // 导入 RandomAccessFile 类
import java.util.ArrayList; // 导入 ArrayList 类
import java.util.HashMap; // 导入 HashMap 类
import java.util.TreeMap; // 导入 TreeMap 类

public class CompressionUtils { // 定义 CompressionUtils 工具类
    private static final long MAX_SIZE = 2 * 1024; // 定义最大文件大小为 2KB
    private static final String RW_MODE = "rw"; // 定义读写模式常量
    private static final String TABLE = ".table"; // 定义表文件扩展名常量

    private final RandomAccessFile file1;
    private final RandomAccessFile file2;

    public CompressionUtils(RandomAccessFile file1, RandomAccessFile file2) { // 构造函数，初始化 file1 和 file2
        this.file1 = file1; // 初始化 file1
        this.file2 = file2; // 初始化 file2
    }


    public void CompressFilesProcess(String dataDir, TreeMap<Integer, HashMap<String, CommandPos>> tableIndex, int tableindex, int newNumber) { // 定义压缩文件方法
        try {


            HashMap<String, CommandPos> index1 = tableIndex.get(tableindex); // 获取旧文件1中的索引
            HashMap<String, CommandPos> index2 = tableIndex.get(tableindex + 1); // 获取旧文件2中的索引

            MergeIndex2(index2, index1); // 将新表中的键添加到旧表index1中

//            System.out.println(file1.length()); // 打印文件1的长度

            ArrayList<String> index1_keys = new ArrayList<>(index1.keySet()); // 打印所有索引键及其位置
//            for (String key : index1_keys) { // 遍历所有键
//                System.out.println(key + " and " + index1.get(key)); // 打印键及其位置
//            }

            int start = 4; // 初始化写入位置为4
            RandomAccessFile Tempfile = new RandomAccessFile(dataDir + "TempDataFile" + newNumber + TABLE, RW_MODE); // 创建新的随机访问文件
            // 遍历所有键，写入新文件
            for (String key : index1_keys) {
                int comPosLength = (int) index1.get(key).getPos();
                // 判断数据是在新表还是旧表中（长度超过就是新表）
                if (comPosLength >= (int) file1.length()) {
                    index1.get(key).setPos(comPosLength - (int) file1.length()); // 更新位置为相对新表的位置
                    start = writeTempDataTable(Tempfile, index1, key, tableindex, dataDir, start); // 写入新数据表，并更新写入位置
                } else {
                    CommandPos cmdPos = index1.get(key); // 获取键对应的命令位置
//                    System.out.println(cmdPos.getPos() + " and " + cmdPos.getLen()); // 打印命令位置及长度
                    byte[] bytes = RandomAccessFileUtil.readByIndex(dataDir + "data" + tableindex + TABLE, cmdPos.getPos(), cmdPos.getLen()); // 从旧文件中读取命令字节数组
                    Tempfile.writeInt(cmdPos.getLen());
                    Tempfile.write(bytes);
                    index1.get(key).setPos(start); // 更新索引中该键的写入位置
                    start += (4 + cmdPos.getLen()); // 更新写入位置
                }
            }

            file1.close();
            file2.close();
            Tempfile.close();

//            System.out.println(index1);
            tableIndex.put(newNumber, index1); // 将更新后的索引存储到 tableIndex 中
            //合并完删除文件，将新创建文件重命名为
            File deleteFile1 = new File(dataDir + "data" + tableindex + TABLE); // 创建要删除的旧文件1
            File deleteFile2 = new File(dataDir + "data" + (tableindex + 1) + TABLE); // 创建要删除的旧文件2
            boolean file1Deleted = deleteFile1.delete();
            boolean file2Deleted = deleteFile2.delete();
//            System.out.println(file1Deleted);
//            System.out.println(file2Deleted);

            File oldfile = new File(dataDir + "TempDataFile" + newNumber + TABLE);
            File newfile = new File(dataDir + "data" + newNumber + TABLE);
            oldfile.renameTo(newfile); // 重命名新文件为目标文件

        } catch (IOException e) { // 捕获 IOException 异常
            throw new RuntimeException(e); // 抛出运行时异常
        }
    }
    //将新表的索引表放到旧表
    private void MergeIndex2(HashMap<String, CommandPos> index2, HashMap<String, CommandPos> index1) { // 定义查找并合并索引键的方法
        try{
            ArrayList<String> index2_keys = new ArrayList<>(index2.keySet());
            for(String key:index2_keys){
//                System.out.println(index2.get(key));
                int oldPos = (int) index2.get(key).getPos();
                index2.get(key).setPos(oldPos+(int)file1.length());
                index1.put(key,index2.get(key));
            }
        }catch(Exception e){
            System.out.println(e.getMessage());
        }
    }

    private int writeTempDataTable(RandomAccessFile file, HashMap<String, CommandPos> index1, String key, int tableindex, String dataDir, int start) throws IOException { // 定义写入新数据表的方法
        CommandPos cmdPos = index1.get(key); // 获取键对应的命令位置
        byte[] bytes = RandomAccessFileUtil.readByIndex(dataDir + "data" + (tableindex + 1) + TABLE, cmdPos.getPos(), cmdPos.getLen()); // 从新文件中读取命令字节数组
        JSONObject value = JSONObject.parseObject(new String(bytes));
        Command cmd = CommandUtil.jsonToCommand(value);
        //判断键相同，命令类型是否相同，如同为set，先rm后set
        if(cmd instanceof SetCommand){
            file.writeInt(cmdPos.getLen());
            file.write(bytes);
            index1.get(key).setPos(start); // 更新索引中命令的写入位置
            start += (4 + cmdPos.getLen()); // 更新写入位置
        }
        if(cmd instanceof RmCommand){
            index1.remove(key);
        }

        return start; // 返回新的写入位置
    }
}
